/***************************************************************************************
* Copyright (c) 2014-2021 Zihao Yu, Nanjing University
* Copyright (c) 2020-2022 Institute of Computing Technology, Chinese Academy of Sciences
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

bool fp_enable();
static int table_op_fp_d(Decode *s);
static int table_fmadd_d_dispatch(Decode *s);

static inline def_DopHelper(fr){
  op->preg = &fpreg_l(val);
  print_Dop(op->str, OP_STR_SIZE, "%s", fpreg_name(val, 4));
#ifdef CONFIG_RVV
  op->reg = val;
#endif // CONFIG_RVV
}

static inline def_DHelper(fr) {
  decode_op_fr(s, id_src1, s->isa.instr.fp.rs1, false);
  decode_op_fr(s, id_src2, s->isa.instr.fp.rs2, false);
  decode_op_fr(s, id_dest, s->isa.instr.fp.rd,  false);
}

static inline def_DHelper(R4) {
  decode_op_fr(s, id_src1, s->isa.instr.fp.rs1, false);
  decode_op_fr(s, id_src2, s->isa.instr.fp.rs2, false);
  decode_op_fr(s, id_dest, s->isa.instr.fp.rd,  false);
  // rs3 is decoded at exec.h
}

static inline def_DHelper(fload) {
  decode_op_r(s, id_src1, s->isa.instr.i.rs1, true);
  decode_op_i(s, id_src2, (sword_t)s->isa.instr.i.simm11_0, false);
  decode_op_fr(s, id_dest, s->isa.instr.i.rd, false);
}

static inline def_DHelper(fstore) {
  decode_op_r(s, id_src1, s->isa.instr.s.rs1, true);
  sword_t simm = (s->isa.instr.s.simm11_5 << 5) | s->isa.instr.s.imm4_0;
  decode_op_i(s, id_src2, simm, false);
  decode_op_fr(s, id_dest, s->isa.instr.s.rs2, false);
}

static inline def_DHelper(fr2r){
  decode_op_fr(s, id_src1, s->isa.instr.fp.rs1, true);
  decode_op_fr(s, id_src2, s->isa.instr.fp.rs2, true);
  decode_op_r(s, id_dest, s->isa.instr.fp.rd, false);
}

static inline def_DHelper(r2fr){
  decode_op_r(s, id_src1, s->isa.instr.fp.rs1, true);
  decode_op_r(s, id_src2, s->isa.instr.fp.rs2, true);
  decode_op_fr(s, id_dest, s->isa.instr.fp.rd, false);
}

#ifdef CONFIG_RVV
bool vp_enable();
def_THelper(vload) {
  def_INSTR_TAB("??? 000 ? ?0000 ????? ??? ????? ????? ??", vle);     // VLE
  def_INSTR_TAB("??? 000 ? 01000 ????? ??? ????? ????? ??", vlr);     // VLR
  def_INSTR_TAB("??? 000 ? 01011 ????? ??? ????? ????? ??", vlm);     // VLM
  def_INSTR_TAB("??? 001 ? ????? ????? ??? ????? ????? ??", vlxe);    // VLUXEI
  def_INSTR_TAB("??? 010 ? ????? ????? ??? ????? ????? ??", vlse);    // VLSE
  def_INSTR_TAB("??? 011 ? ????? ????? ??? ????? ????? ??", vlxe);    // VLOXEI
  return EXEC_ID_inv;
}

def_THelper(vstore) {
  decode_op_i(s, id_src2, (sword_t)s->isa.instr.i.simm11_0, false);
  decode_op_fr(s, id_dest, s->isa.instr.i.rd, false);
  def_INSTR_TAB("??? 000 ? 00000 ????? ??? ????? ????? ??", vse);     // VSE
  def_INSTR_TAB("??? 000 ? 01000 ????? ??? ????? ????? ??", vsr);     // VSR
  def_INSTR_TAB("??? 000 ? 01011 ????? ??? ????? ????? ??", vsm);     // VSM
  def_INSTR_TAB("??? 001 ? ????? ????? ??? ????? ????? ??", vsxe);    // VSUXEI
  def_INSTR_TAB("??? 010 ? ????? ????? ??? ????? ????? ??", vsse);    // VSSE
  def_INSTR_TAB("??? 011 ? ????? ????? ??? ????? ????? ??", vsxe);    // VSOXEI
  return EXEC_ID_inv;
}

def_THelper(vload_mmu) {
  def_INSTR_TAB("??? 000 ? ?0000 ????? ??? ????? ????? ??", vle_mmu);   // VLE
  def_INSTR_TAB("??? 000 ? 01000 ????? ??? ????? ????? ??", vlr_mmu);   // VLR
  def_INSTR_TAB("??? 000 ? 01011 ????? ??? ????? ????? ??", vlm_mmu);   // VLM
  def_INSTR_TAB("??? 001 ? ????? ????? ??? ????? ????? ??", vlxe_mmu);  // VLUXEI
  def_INSTR_TAB("??? 010 ? ????? ????? ??? ????? ????? ??", vlse_mmu);  // VLSE
  def_INSTR_TAB("??? 011 ? ????? ????? ??? ????? ????? ??", vlxe_mmu);  // VLOXEI
  return EXEC_ID_inv;
}

def_THelper(vstore_mmu) {
  decode_op_i(s, id_src2, (sword_t)s->isa.instr.i.simm11_0, false);
  decode_op_fr(s, id_dest, s->isa.instr.i.rd, false);
  def_INSTR_TAB("??? 000 ? 00000 ????? ??? ????? ????? ??", vse_mmu);   // VSE
  def_INSTR_TAB("??? 000 ? 01000 ????? ??? ????? ????? ??", vsr_mmu);   // VSR
  def_INSTR_TAB("??? 000 ? 01011 ????? ??? ????? ????? ??", vsm_mmu);   // VSM
  def_INSTR_TAB("??? 001 ? ????? ????? ??? ????? ????? ??", vsxe_mmu);  // VSUXEI
  def_INSTR_TAB("??? 010 ? ????? ????? ??? ????? ????? ??", vsse_mmu);  // VSSE
  def_INSTR_TAB("??? 011 ? ????? ????? ??? ????? ????? ??", vsxe_mmu);  // VSOXEI
  return EXEC_ID_inv;
}

#endif

def_THelper(fload) {
  print_Dop(id_src1->str, OP_STR_SIZE, "%ld(%s)", id_src2->imm, reg_name(s->isa.instr.i.rs1, 4));

  int mmu_mode = isa_mmu_state();
  if (mmu_mode == MMU_DIRECT) {
    if (fp_enable()) {
#ifndef CONFIG_FPU_NONE
      def_INSTR_TAB("??????? ????? ????? 010 ????? ????? ??", flw);
      def_INSTR_TAB("??????? ????? ????? 011 ????? ????? ??", fld);
#endif // CONFIG_FPU_NONE
    }

    #ifdef CONFIG_RVV
    if (vp_enable()) {
      def_INSTR_TAB("??????? ????? ????? 000 ????? ????? ??", vload);
      def_INSTR_TAB("??????? ????? ????? 101 ????? ????? ??", vload);
      def_INSTR_TAB("??????? ????? ????? 110 ????? ????? ??", vload);
      def_INSTR_TAB("??????? ????? ????? 111 ????? ????? ??", vload);
    }
    #endif // CONFIG_RVV
  } else if (mmu_mode == MMU_TRANSLATE) {
    if (fp_enable()) {
#ifndef CONFIG_FPU_NONE
      def_INSTR_TAB("??????? ????? ????? 010 ????? ????? ??", flw_mmu);
      def_INSTR_TAB("??????? ????? ????? 011 ????? ????? ??", fld_mmu);
#endif // CONFIG_FPU_NONE
    }

    #ifdef CONFIG_RVV
    if (vp_enable()) {
      def_INSTR_TAB("??????? ????? ????? 000 ????? ????? ??", vload_mmu);
      def_INSTR_TAB("??????? ????? ????? 101 ????? ????? ??", vload_mmu);
      def_INSTR_TAB("??????? ????? ????? 110 ????? ????? ??", vload_mmu);
      def_INSTR_TAB("??????? ????? ????? 111 ????? ????? ??", vload_mmu);
    }
    #endif // CONFIG_RVV
  } // else { assert(0); }
  return EXEC_ID_inv;
}

def_THelper(fstore) {
  print_Dop(id_src1->str, OP_STR_SIZE, "%ld(%s)", id_src2->imm, reg_name(s->isa.instr.i.rs1, 4));

  int mmu_mode = isa_mmu_state();
  if (mmu_mode == MMU_DIRECT) {
#ifndef CONFIG_FPU_NONE
    if (fp_enable()) {
      def_INSTR_TAB("??????? ????? ????? 010 ????? ????? ??", fsw);
      def_INSTR_TAB("??????? ????? ????? 011 ????? ????? ??", fsd);
    }
#endif // CONFIG_FPU_NONE

    #ifdef CONFIG_RVV
    if (vp_enable()) {
      def_INSTR_TAB("??????? ????? ????? 000 ????? ????? ??", vstore);
      def_INSTR_TAB("??????? ????? ????? 101 ????? ????? ??", vstore);
      def_INSTR_TAB("??????? ????? ????? 110 ????? ????? ??", vstore);
      def_INSTR_TAB("??????? ????? ????? 111 ????? ????? ??", vstore);
    }
    #endif // CONFIG_RVV
  } else if (mmu_mode == MMU_TRANSLATE) {
#ifndef CONFIG_FPU_NONE
    if (fp_enable()) {
      def_INSTR_TAB("??????? ????? ????? 010 ????? ????? ??", fsw_mmu);
      def_INSTR_TAB("??????? ????? ????? 011 ????? ????? ??", fsd_mmu);
    }
#endif // CONFIG_FPU_NONE

    #ifdef CONFIG_RVV
    if (vp_enable()) {
      def_INSTR_TAB("??????? ????? ????? 000 ????? ????? ??", vstore_mmu);
      def_INSTR_TAB("??????? ????? ????? 101 ????? ????? ??", vstore_mmu);
      def_INSTR_TAB("??????? ????? ????? 110 ????? ????? ??", vstore_mmu);
      def_INSTR_TAB("??????? ????? ????? 111 ????? ????? ??", vstore_mmu);
    }
    #endif // CONFIG_RVV
  } // else { assert(0); }
  return EXEC_ID_inv;
}

def_THelper(op_fp) {
#ifndef CONFIG_FPU_NONE
  if (!fp_enable()) return table_rt_inv(s);

  if ((s->isa.instr.fp.fmt == 0b00 && s->isa.instr.fp.funct5 == 0b01000) ||
      s->isa.instr.fp.fmt == 0b01) return table_op_fp_d(s);

  // RV32F
  def_INSTR_IDTAB("00000 00 ????? ????? ??? ????? ????? ??", fr  , fadds);
  def_INSTR_IDTAB("00001 00 ????? ????? ??? ????? ????? ??", fr  , fsubs);
  def_INSTR_IDTAB("00010 00 ????? ????? ??? ????? ????? ??", fr  , fmuls);
  def_INSTR_IDTAB("00011 00 ????? ????? ??? ????? ????? ??", fr  , fdivs);
  def_INSTR_IDTAB("01011 00 00000 ????? ??? ????? ????? ??", fr  , fsqrts);
  def_INSTR_IDTAB("00100 00 ????? ????? 000 ????? ????? ??", fr  , fsgnjs);
  def_INSTR_IDTAB("00100 00 ????? ????? 001 ????? ????? ??", fr  , fsgnjns);
  def_INSTR_IDTAB("00100 00 ????? ????? 010 ????? ????? ??", fr  , fsgnjxs);
  def_INSTR_IDTAB("00101 00 ????? ????? 000 ????? ????? ??", fr  , fmins);
  def_INSTR_IDTAB("00101 00 ????? ????? 001 ????? ????? ??", fr  , fmaxs);
  def_INSTR_IDTAB("11000 00 00000 ????? ??? ????? ????? ??", fr2r, fcvt_w_s);
  def_INSTR_IDTAB("11000 00 00001 ????? ??? ????? ????? ??", fr2r, fcvt_wu_s);
  def_INSTR_IDTAB("11100 00 00000 ????? 000 ????? ????? ??", fr2r, fmv_x_w);
  def_INSTR_IDTAB("10100 00 ????? ????? 010 ????? ????? ??", fr2r, feqs);
  def_INSTR_IDTAB("10100 00 ????? ????? 001 ????? ????? ??", fr2r, flts);
  def_INSTR_IDTAB("10100 00 ????? ????? 000 ????? ????? ??", fr2r, fles);
  def_INSTR_IDTAB("11100 00 00000 ????? 001 ????? ????? ??", fr2r, fclasss);
  def_INSTR_IDTAB("11010 00 00000 ????? ??? ????? ????? ??", r2fr, fcvt_s_w);
  def_INSTR_IDTAB("11010 00 00001 ????? ??? ????? ????? ??", r2fr, fcvt_s_wu);
  def_INSTR_IDTAB("11110 00 00000 ????? 000 ????? ????? ??", r2fr, fmv_w_x);

  // RV32F
  def_INSTR_IDTAB("11000 00 00010 ????? ??? ????? ????? ??", fr2r, fcvt_l_s);
  def_INSTR_IDTAB("11000 00 00011 ????? ??? ????? ????? ??", fr2r, fcvt_lu_s);
  def_INSTR_IDTAB("11010 00 00010 ????? ??? ????? ????? ??", r2fr, fcvt_s_l);
  def_INSTR_IDTAB("11010 00 00011 ????? ??? ????? ????? ??", r2fr, fcvt_s_lu);
#endif // CONFIG_FPU_NONE

  return EXEC_ID_inv;
}

def_THelper(fmadd_dispatch) {
#ifndef CONFIG_FPU_NONE
  if (!fp_enable()) return table_rt_inv(s);
  def_INSTR_TAB("????? 01 ????? ????? ??? ????? ????? ??", fmadd_d_dispatch);

  def_INSTR_TAB("????? 00 ????? ????? ??? ????? 10000 ??", fmadds);
  def_INSTR_TAB("????? 00 ????? ????? ??? ????? 10001 ??", fmsubs);
  def_INSTR_TAB("????? 00 ????? ????? ??? ????? 10010 ??", fnmsubs);
  def_INSTR_TAB("????? 00 ????? ????? ??? ????? 10011 ??", fnmadds);
#endif // CONFIG_FPU_NONE
  return EXEC_ID_inv;
}
